/**
 * 	 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *   @author Colin Redmond
 *   Copyright (C) 2011 Colin Redmond
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.cred.industries.platform.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.TimeZone;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import com.cred.industries.core.database.DBConnectionManager;
import com.cred.industries.core.exceptions.ApplicationException;
import com.cred.industries.core.transferObj.result.ReturnCodes;
import com.cred.industries.platform.business.objects.SessionBO;

/**
 * manages all DB interaction for the session. It takes in 
 * session BO's to update/create the DB
 * Note this class has no modify so it is only accessible in this package
 */
class SessionMySQLDAO implements ISessionDAO {
	
	//private final static Logger logger = LoggerFactory.getLogger(SessionMySQLDAO.class);
	
	private static final String SESSION_ID = "sessionid";
	private static final String CUSTOMER_ID = "customerId";
	private static final String TIME_ISSUED = "timeIssued";
	private static final String LAST_REQUEST = "lastRequestTime";
	
	/* (non-Javadoc)
	 * @see com.cred.industries.platform.dao.ISessionDAO#insert(com.cred.industries.platform.business.objects.SessionBO)
	 */
	@Override
	public void insert(SessionBO sessionBO) {
		
		// Setup the connection with the DB
		try (Connection connect = DBConnectionManager.getInstance().getConnection()) {
			insert(connect, sessionBO);
		} catch (SQLException e) {
			throw new ApplicationException(e, ReturnCodes.DATABASE_ERROR, "failed to create session with " + sessionBO.getCustomerId());
		} 
		
	}
	
	/* (non-Javadoc)
	 * @see com.cred.industries.platform.dao.ISessionDAO#insert(java.sql.Connection, com.cred.industries.platform.business.objects.SessionBO)
	 */
	@Override
	public void insert(Connection connect, SessionBO sessionBO) {
		
		String sqlQuery = "INSERT INTO session VALUES(?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())";
		
		try (PreparedStatement preparedStatement = connect.prepareStatement(sqlQuery)) {

			preparedStatement.setString(1, sessionBO.getSessionId());
			preparedStatement.setInt(2, sessionBO.getCustomerId());
			
			int rowUpdated = preparedStatement.executeUpdate();
			if(rowUpdated == 0) {
				throw new ApplicationException(ReturnCodes.DATABASE_ERROR, "failed to create session with " + sessionBO.getCustomerId() +  " no rows updated");
			}
			
		} catch (SQLException e) {
			throw new ApplicationException(e, ReturnCodes.DATABASE_ERROR, "failed to create session with " + sessionBO.getCustomerId());
		}
	}
	

	/**
	 * pdate the Session BO in the DB, this will only update the TIME_ISSUED and LAST_REQUEST
	 * @param sessionBO the session BO to be updated
	 */
	public void update(SessionBO sessionBO) {
		
		try (Connection connect = DBConnectionManager.getInstance().getConnection()) {
			update(connect, sessionBO);
		} catch (SQLException e) {
			throw new ApplicationException(e, ReturnCodes.DATABASE_ERROR, "failed to update session with " + sessionBO.getCustomerId());
		} 
		
	}
	
	/**
	 * update the Session BO in the DB, this will only update the TIME_ISSUED and LAST_REQUEST
	 * @param connect the DB connection used to insert, assumed to be a session. Caller must manager
	 * @param sessionBO the session BO to be updated
	 */
	public void update(Connection connect, SessionBO sessionBO) {
		
		String sqlQuery = "UPDATE session SET " + TIME_ISSUED + " = ?, " +  LAST_REQUEST + " = ? "
				  + "WHERE " + SESSION_ID + " =? ";
		try (PreparedStatement preparedStatement = connect.prepareStatement(sqlQuery)) {

			Timestamp issued = new Timestamp(sessionBO.getTimeIssued().getMillis());
			preparedStatement.setTimestamp(1, issued, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
			
			Timestamp lastReq = new Timestamp(sessionBO.getLastRequest().getMillis());
			preparedStatement.setTimestamp(2, lastReq, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
			
			preparedStatement.setString(3, sessionBO.getSessionId());

			
			int rowUpdated = preparedStatement.executeUpdate();
			if(rowUpdated == 0) {
				throw new ApplicationException(ReturnCodes.DATABASE_ERROR, "failed to update session with " + sessionBO.getCustomerId() +  " no rows updated");
			}
			
		} catch (SQLException e) {
			throw new ApplicationException(e, ReturnCodes.DATABASE_ERROR, "failed to update session with " + sessionBO.getCustomerId());
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cred.industries.platform.dao.ISessionDAO#findSessionById(java.lang.String)
	 */
	@Override
	public SessionBO findSessionById(String sessionId) {

		if(sessionId == null)
			return null;
			
		SessionBO result = null;
		String sqlQuery = "SELECT * FROM session WHERE sessionid = ?";
		
		try (Connection connect = DBConnectionManager.getInstance().getConnection();
				PreparedStatement preparedStatement = connect.prepareStatement(sqlQuery);) {
			
			preparedStatement.setString(1, sessionId);			
			
			try (ResultSet resultSet = preparedStatement.executeQuery()) {
			
				if(!resultSet.next()) {
					return null;
				}
	
				Timestamp issued = resultSet.getTimestamp(TIME_ISSUED, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
				Timestamp lastReq = resultSet.getTimestamp(LAST_REQUEST, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
				
				result = new SessionBO(resultSet.getString(SESSION_ID)
								,resultSet.getInt(CUSTOMER_ID)
								, new DateTime(issued, DateTimeZone.UTC)
								, new DateTime(lastReq, DateTimeZone.UTC));
				
				return result;
			}
		} catch (SQLException e) {
			throw new ApplicationException(e, ReturnCodes.DATABASE_ERROR, "failed to populate session with " + sessionId);
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cred.industries.platform.dao.ISessionDAO#delete(com.cred.industries.platform.business.objects.SessionBO)
	 */
	@Override
	public boolean delete(SessionBO sessionBO) {
		
		if(sessionBO == null || sessionBO.getSessionId().length() == 0)
			throw new IllegalArgumentException(ReturnCodes.INVALID_SESSION.toString());
		
		String sqlQuery = "DELETE FROM session where sessionid = ?";
		try (Connection connect = DBConnectionManager.getInstance().getConnection();
				PreparedStatement preparedStatement = connect.prepareStatement(sqlQuery);) {
			
			preparedStatement.setString(1, sessionBO.getSessionId());
			
			int rowAltered = preparedStatement.executeUpdate();
			
			if(rowAltered == 0) {
				return false;
			}
			
			return true;
			
		} catch (SQLException e) {
			throw new ApplicationException(e, ReturnCodes.DATABASE_ERROR, "failed to delete session from " + sessionBO.getCustomerId() + " session ID: " + sessionBO.getSessionId());
		} 
	}
}
